
// ===============================================================================================================
// -*- C++ -*-
//
// MathLib.hpp - Fast 3D math routines.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#ifndef __MATHLIB_HPP__
#define __MATHLIB_HPP__

namespace MathLib {

// Forward declarations (Defined below in this file).
class Vec2f;
class Vec3f;

// =========================================================
// Usefull Mathematical Constants
// =========================================================

extern const float PI;         ///< 4*atan(1)
extern const float TWO_PI;     ///< 2*pi
extern const float HALF_PI;    ///< pi/2
extern const float INV_PI;     ///< 1/pi
extern const float INV_TWO_PI; ///< 1/(2*pi)
extern const float DEG_TO_RAD; ///< pi/180
extern const float RAD_TO_DEG; ///< 180/pi

// =========================================================
// Trigonometry - All Angles Are In Radians!
// =========================================================

// Sine of angle in radians.
float Sine(float x);

// Cosine of angle in radians.
float Cosine(float x);

// Computes the sine and cosine of the angle using a single FP instruction if available.
void SineCosine(float ang, float & s, float & c);

// Arc sine. Clamp the input to [-1,1] to avoid NAN issues.
float ArcSine(float x);

// Arc cosine. Clamp the input to [-1,1] to avoid NAN issues.
float ArcCosine(float x);

// Tangent of angle in radians.
float Tangent(float x);

// Returns angle between [-pi/2,+pi/2] radians.
float ArcTangent(float x);

// Arctangent of (x/y) with correct sign. Returns angle between [-pi,+pi] radians.
float ArcTangent(float x, float y);

// Computes the hypotenuse of x and y.
float Hypotenuse(float x, float y);

// =========================================================
// Floating Point Arithmetics
// =========================================================

// Square root.
float Sqrt(float x);

// Inverse square root. (Reciprocal square root).
float InvSqrt(float x);

// Returns a decimal value representing the smallest integer that is greater than or equal to x. 
float Ceil(float x);

// Returns a decimal value representing the largest integer that is less than or equal to x.
float Floor(float x);

// Truncates the decimal value to an integer.
int Truncate(float x);

// Checks if the value is NAN.
bool IsNAN(float x);

// =========================================================
// Random Numbers
// =========================================================

// Seeds the random number generator with the current time.
void SeedRandomGenerator(void);

// Generates a random number in the range of [0,1].
float UniformRandom(void);

// Generates a random number in the range of [min,max].
int RandomNumber(int min, int max);

// =========================================================
// Interpolation Functions
// =========================================================

// Linear interpolation between vectors. (Vec2f)
Vec2f LinearInterpolation(const Vec2f & a, const Vec2f & b, float t);

// Linear interpolation between vectors. (Vec3f)
Vec3f LinearInterpolation(const Vec3f & a, const Vec3f & b, float t);

// Hermite interpolation between vectors.
Vec3f HermiteInterpolation(const Vec3f & y0, const Vec3f & y1, const Vec3f & y2, const Vec3f & y3, float t, float tension, float bias);

// =========================================================
// Templates / Inline Routines
// =========================================================

// Absolute value of x.
template<typename T> static inline T Abs(T x)
{
	return ((x < T(0)) ? -x : x);
}

// Minimum value between a and b.
template<typename T> static inline T Min(T a, T b)
{
	return ((a < b) ? a : b);
}

// Maximum value between a and b.
template<typename T> static inline T Max(T a, T b)
{
	return ((a > b) ? a : b);
}

// Clamping
template<typename T> inline T Clamp(T x, T minimum, T maximum)
{
	return ((x < minimum) ? minimum : (x > maximum) ? maximum : x);
}

// Return the sign of a number
template<typename T> inline T Sign(T x)
{
	return ((x > T(0)) ? T(1) : (x < T(0)) ? T(-1) : 0);
}

#if defined (_MSC_VER)
/* Nnonstandard extension used : nameless struct/union */
#pragma warning (disable: 4201)
#endif // _MSC_VER

// =========================================================
// 2D Vector Class
// =========================================================

class Vec2f {

public:

	union {
		struct {
			float x; // X-Axis
			float y; // Y-Axis
		};
		float v[2];
	};

public:

	// Constructors:

	Vec2f(void) { /* Leave uninitialized */ };
	Vec2f(float X, float Y);
	Vec2f(const Vec2f & r);

	// Operators:

	Vec2f & operator = (float Val);
	Vec2f & operator = (const Vec2f & r);

	bool operator == (const Vec2f & r) const;
	bool operator != (const Vec2f & r) const;

public:

	// Public Interface:

	// Linear interpolation between this vector and 'vec'.
	Vec2f Lerp(const Vec2f & vec, float t) const;

	// Dot product between this vector and 'r'.
	float DotProduct(const Vec2f & r) const;

	// The absolute length of this vector.
	float Length(void) const;

	// Normalize this vector. (Divide it by it's length).
	void Normalize(void);

	// Scale the vector by a given factor. (vector = vector * factor)
	void Scale(float factor);

	// Negate the vector. (vector = -vector)
	void Negate(void);
};

// =========================================================
// 3D Vector Class
// =========================================================

class Vec3f {

public:

	union {
		struct {
			float x; // X-Axis
			float y; // Y-Axis
			float z; // Z-Axis
		};
		float v[3];
	};

public:

	// Constructors:

	Vec3f(void) { /* Leave uninitialized */ };
	Vec3f(float X, float Y, float Z);
	Vec3f(const Vec3f & r);

	// Operators:

	Vec3f & operator = (float Val);
	Vec3f & operator = (const Vec3f & r);

	bool operator == (const Vec3f & r) const;
	bool operator != (const Vec3f & r) const;

	// Arithmetic operations:

	Vec3f operator + (const Vec3f & vec) const;
	Vec3f operator - (const Vec3f & vec) const;
	Vec3f operator * (float scalar) const;
	Vec3f operator / (float scalar) const;
	Vec3f operator - (void) const;

	// Arithmetic updates:

	Vec3f & operator += (const Vec3f & vec);
	Vec3f & operator -= (const Vec3f & vec);
	Vec3f & operator *= (float scalar);
	Vec3f & operator /= (float scalar);

	friend Vec3f operator * (float scalar, const Vec3f & vec);
	friend Vec3f operator / (float scalar, const Vec3f & vec);

public:

	// Public Interface:

	// Dot product between this vector and 'r'.
	float DotProduct(const Vec3f & r) const;

	// The absolute length of this vector.
	float Length(void) const;

	// Computes the cross product of this vector and 'vec' and return the result as a new vector.
	Vec3f CrossProduct(const Vec3f & vec) const;

	// Angle in radians between this vector and 'vec'.
	float Angle(const Vec3f & v) const;

	// Rotate this vector arround the normalized 'axis' by 'angle' radians.
	void Rotate(const Vec3f & axis, float angle);

	// Normalize this vector. (Divide it by it's length).
	void Normalize(void);

	// Scale the vector by a given factor. (vector = vector * factor)
	void Scale(float factor);

	// Negate the vector. (vector = -vector)
	void Negate(void);
};

#if defined (_MSC_VER)
#pragma warning (default: 4201)
#endif // _MSC_VER

}; // namespace MathLib {}

#endif // __MATHLIB_HPP__